package states
{
	import flash.utils.*;
	import utilities.Wave;
	
	import org.flixel.*;
	
	import items.*;
	import interactions.*;
	import enemies.*;
	import treasures.*;
	import projectiles.*;
	
	/**
	 * ...
	 * @author JAC
	 */
	public class GameState extends PlayState
	{
		protected var _waitForPlayer:Boolean;
		protected var _checkGameOver:Boolean;
		
		protected var _waves:Vector.<Wave>;
		protected var _currentWave:Wave;
		
		private var _pauseScreen:PauseScreen;
		
		override public function create():void
		{
			super.create();
			
			includeLayers();
			Registry.heatMap.clear();
			Tracker.init();
			
			_waitForPlayer = true;
			_checkGameOver = true;
			
			_waves = new Vector.<Wave>();
			_currentWave = null;
			
			MusicPlayer.play(MusicPlayer.TOMB_FAST);
			
			_pauseScreen = PauseScreen.add();
		}
		
		override public function update():void
		{
			waitForPlayer();
			
			if (Registry.paused)
			{
				_pauseScreen.update();
				
				if (_pauseScreen.returnToMainMenu())
				{
					logQuitWithMessage("main");
					_pauseScreen.disabled = true;
					FlxG.fade(0xffaaaaaa, 2, backToMainMenu);
				}
				
				if (_pauseScreen.restartLevel())
				{
					logQuitWithMessage("restart");
					_pauseScreen.disabled = true;
					FlxG.fade(0xffaaaaaa, 2, restartLevel);
				}
				
				return;
			}
			
			super.update();
			
			FlxG.collide(_enemies, _edge, enemyEdgeCollide);
			FlxG.collide(_projectiles, _maze);
			FlxG.collide(_projectiles, _edge);
			
			FlxG.overlap(_player, _enemies, playerEnemyOverlap);
			
			FlxG.overlap(_player, _interactions, playerInteractionOverlap);
			
			FlxG.overlap(_player, _items, playerItemOverlap);
			FlxG.overlap(_enemies, _interactions, enemyInteractionOverlap);
			FlxG.overlap(_enemies, _treasures, enemyTreasureOverlap);
			FlxG.overlap(_enemies, _projectiles, enemyProjectileOverlap);
			
			checkGameOver();
			
			if (_waves.length > 0)
			{
				if (_currentWave.timerFinished && beatable())
				{
					_currentWave = _waves.shift();
					add(_currentWave);
					for (var a:uint = 0; a < _currentWave.entities.length; a++)
					{
						var b:Entity = _currentWave.entities[a];
						addEntity(b);
					}
					Tracker.wave++;
				}
				else if (_currentWave.ended && beatable())
				{
					_currentWave.playWarning();
				}
			}
		}
		
		protected function beatable():Boolean
		{
			return Tracker.score >= Tracker.minScore;
		}
		
		protected function gameStart():void
		{
			Tracker.tallyTreasures();
			_ui.resetText();
			
			var lg:LevelGrave = Registry.levelGraveMap[getQualifiedClassName(this)];
			if (lg != null)
			{
				LoggerWrapper.logLevelStart(lg.ID, lg.prettyName);
			}
		}
		
		protected function logGameEnd():void
		{
			var lg:LevelGrave = Registry.levelGraveMap[getQualifiedClassName(this)];
			if (lg != null)
			{
				LoggerWrapper.logLevelEnd(Tracker.log(lg));
			}
		}
		
		protected function logQuitWithMessage(message:String):void
		{
			var lg:LevelGrave = Registry.levelGraveMap[getQualifiedClassName(this)];
			
			if (lg != null)
			{
				var d:Array = Tracker.log(lg);
				d[d.length - 2] = message;
				LoggerWrapper.logLevelEnd(d);
			}
		}
		
		protected function addWave(wave:Wave, delayInMillis:Number = 1.5):void
		{
			wave.delay = delayInMillis;
			if (_currentWave == null)
			{
				_currentWave = wave;
				add(_currentWave);
				for (var a:uint = 0; a < _currentWave.entities.length; a++)
				{
					var b:Entity = _currentWave.entities[a];
					addEntity(b);
				}
			}
			else
			{
				_waves.push(wave);
			}
		}
		
		private function includeLayers():void
		{
			includeLayer(_edge);
			includeLayer(_interactions);
			includeLayer(_enemies);
			includeLayer(_items);
			includeLayer(_treasures);
			includeLayer(_projectiles);
			//includeLayer(_lights);
			//includeLayer(_darkness);
			includeLayer(_misc);
			includeLayer(_animations);
			includeLayer(Registry.heatMap);
		}
		
		private function waitForPlayer():void
		{
			if (_waitForPlayer)
			{
				Registry.paused = true;
				if (FlxG.keys.any())
				{
					_waitForPlayer = false
					Registry.paused = false;
				}
				
				_animations.update();
				_ui.update();
				_lights.update();
			}
		}
		
		private function restartLevel():void
		{
			FlxG.resetState();
		}
		
		private function backToMainMenu():void
		{
			FlxG.switchState(new MenuState());
		}
		
		private function checkGameOver():void
		{
			if (_checkGameOver)
			{
				if (!_player.exists || (_enemies.length <= 0 && _waves.length <= 0) || (_enemies.length <= 0 && !beatable()))
				{
					_pauseScreen.disabled = true;
					FlxG.fade(0xffaaaaaa, 2, gameOver);
				}
			}
		}
		
		private function gameOver():void
		{
			Tracker.levelComplete = _player.exists && (Tracker.score >= Tracker.minScore);
			
			logGameEnd();
			
			FlxG.switchState(new ScoreState(Registry.levelGraveMap[getQualifiedClassName(this)]));
		}
		
		private function enemyEdgeCollide(e:Enemy, edge:FlxTilemap):void
		{
			e.escapes();
		}
		
		private function playerEnemyOverlap(p:Player, e:Enemy):void
		{
			if (e.alive && Player.isAlive && overlapThreshold(p, e))
			{
				e.overlapWithPlayer(p);
			}
		}
		
		private function playerInteractionOverlap(p:Player, i:Interaction):void
		{
			i.overlapWithPlayer(p);
		}
		
		private function playerItemOverlap(p:Player, i:Item):void
		{
			if (overlapThreshold(p, i))
			{
				if (_ui.insertItem(i))
				{
					_items.remove(i, true);
					Tracker.itemsPickedUp++;
					FlxG.play(Assets.SndBackpack, Registry.soundVolume);
				}
			}
		}
		
		private function enemyInteractionOverlap(e:Enemy, i:Interaction):void
		{
			if (e.alive && overlapThreshold(e, i, 15))
			{
				i.overlapWithEnemy(e);
			}
		}
		
		private function enemyTreasureOverlap(e:Enemy, t:Treasure):void
		{
			if (e.alive && overlapThreshold(e, t))
			{
				e.overlapWithTreasure(t);
				t.overlapWithEnemy(e);
			}
		}
		
		private function enemyProjectileOverlap(e:Enemy, p:Projectile):void
		{
			if (e.alive && p.alive && overlapThreshold(e, p))
			{
				e.overlapWithProjectile(p);
				p.overlapWithEnemy(e);
			}
		}
	}
}